package com.nsharmon.dao.entity;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OrderColumn;
import javax.persistence.Transient;

import org.hibernate.Session;
import org.hibernate.annotations.GenericGenerator;

import com.google.gson.annotations.Expose;

@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="type", discriminatorType=DiscriminatorType.STRING)
public abstract class Axiom {
	private long id;
	
	private Project project;
	
	private Enum<?> variant;
	
	@Expose	
	private List<EntityObject> entities = new ArrayList<EntityObject>();
	
	@Expose	
	private List<Parameter> parameters = new ArrayList<Parameter>();
	
	public Axiom() {

	}
	
	@Id
	@GeneratedValue(generator="increment")
	@GenericGenerator(name="increment", strategy = "increment")		
	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}
	
	@ManyToOne(fetch = FetchType.LAZY)	
	public Project getProject() {
		return project;
	}
	
	public void setProject(Project project) {
		this.project = project;
	}

	@OrderColumn
	@ManyToMany(fetch = FetchType.EAGER, cascade={ CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH })
	public List<EntityObject> getEntities() {
		return entities;
	}

	public void setEntities(List<EntityObject> entities) {
		this.entities = entities;
	}
	
	@Enumerated(EnumType.STRING)
	public Enum<?> getVariant() {
		return variant;
	}

	public void setVariant(Enum<?> variant) {
		this.variant = variant;
	}

	@OrderColumn
	@OneToMany(fetch = FetchType.EAGER, cascade={ CascadeType.ALL })		
	public List<Parameter> getParameters() {
		return parameters;
	}

	public void setParameters(List<Parameter> parameters) {
		this.parameters = parameters;
	}

	@Transient
	public abstract String toString(Enum<?> variant);
	
	@Override
	@Transient
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((entities == null) ? 0 : entities.hashCode());
		result = prime * result
				+ ((parameters == null) ? 0 : parameters.hashCode());
		result = prime * result + ((variant == null) ? 0 : variant.hashCode());
		return result;
	}

	@Override
	@Transient
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Axiom other = (Axiom) obj;
		if (entities == null) {
			if (other.entities != null)
				return false;
		} else if (!entities.equals(other.entities))
			return false;
		if (parameters == null) {
			if (other.parameters != null)
				return false;
		} else if (!parameters.equals(other.parameters))
			return false;
		if (variant == null) {
			if (other.variant != null)
				return false;
		} else if (!variant.equals(other.variant))
			return false;
		return true;
	}

	@Transient
	public Axiom clone() {
		Axiom axiom = null;
		try {
			axiom = this.getClass().newInstance();
			
			axiom.setEntities(new ArrayList<EntityObject>(getEntities()));
			axiom.setVariant(getVariant());	
			axiom.setParameters(new ArrayList<Parameter>(getParameters()));
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		
		return axiom;	
	}

	@Transient
	public void replace(EntityObject eo, EntityObject eo2) {
		List<EntityObject> entities = getEntities();
		for(int i=0; i<entities.size(); i++) {
			EntityObject obj = entities.get(i);
			if(obj.equals(eo)) {
				entities.set(i, eo2);
			}
		}
	}

	@Transient
	public void addEntity(String entityName) {
		getEntities().add(EntityObject.getOrCreate(entityName));
	}
	
	@Transient
	public void addEntity(Session session, String entityName) {
		getEntities().add(EntityObject.getOrCreate(session, entityName));
	}
}
